/*
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 
 * MA  02110-1301, USA.
 *
 * http://www.gnu.org/copyleft/gpl.html
 */
package com.aionemu.gameserver.model.gameobjects.instance;

import java.util.ArrayList;
import java.util.List;

import org.apache.log4j.Logger;

import com.aionemu.commons.database.dao.DAOManager;
import com.aionemu.gameserver.dao.PlayerBindPointDAO;
import com.aionemu.gameserver.dataholders.DataManager;
import com.aionemu.gameserver.model.Race;
import com.aionemu.gameserver.model.TribeClass;
import com.aionemu.gameserver.model.gameobjects.Item;
import com.aionemu.gameserver.model.gameobjects.VisibleObject;
import com.aionemu.gameserver.model.gameobjects.interfaces.IDialogRequest;
import com.aionemu.gameserver.model.gameobjects.player.BindPointPosition;
import com.aionemu.gameserver.model.gameobjects.player.Player;
import com.aionemu.gameserver.model.gameobjects.player.RequestResponseHandler;
import com.aionemu.gameserver.model.templates.BindPointTemplate;
import com.aionemu.gameserver.model.templates.npcTemplate.NpcTemplate;
import com.aionemu.gameserver.model.templates.spawn.SpawnTemplate;
import com.aionemu.gameserver.network.aion.serverpackets.SM_LEVEL_UPDATE;
import com.aionemu.gameserver.network.aion.serverpackets.SM_QUESTION_WINDOW;
import com.aionemu.gameserver.network.aion.serverpackets.SM_SYSTEM_MESSAGE;
import com.aionemu.gameserver.services.TeleportService;
import com.aionemu.gameserver.taskmanager.AbstractIterativePeriodicTaskManager;
import com.aionemu.gameserver.utils.MathUtil;
import com.aionemu.gameserver.utils.PacketSendUtility;
import com.aionemu.gameserver.world.World;
import com.aionemu.gameserver.world.WorldType;

/**
 * @author Mr. Poke, Lyahim
 *
 */
public class BindPoint extends StaticNpc implements IDialogRequest, Runnable
{

	private static Logger			log		= Logger.getLogger(BindPoint.class);

	private final BindPointTemplate	bindPointTemplate;

	private List<Player>			players	= new ArrayList<Player>();

	private static final class BindPoinTaskManager extends AbstractIterativePeriodicTaskManager<BindPoint>
	{
		private static final BindPoinTaskManager	_instance	= new BindPoinTaskManager();

		static BindPoinTaskManager getInstance()
		{
			return _instance;
		}

		private BindPoinTaskManager()
		{
			super(1000);
		}

		@Override
		protected void callTask(BindPoint task)
		{
			task.run();
		}

		@Override
		protected String getCalledMethodName()
		{
			return "run()";
		}
	}

	/* (non-Javadoc)
	 * @see com.aionemu.gameserver.model.gameobjects.instance.StaticNpc#see(com.aionemu.gameserver.model.gameobjects.VisibleObject)
	 */
	@Override
	public void see(VisibleObject object)
	{
		super.see(object);
		if (object instanceof Player && !BindPoinTaskManager.getInstance().hasTask(this))
			BindPoinTaskManager.getInstance().startTask(this);
	}

	/* (non-Javadoc)
	 * @see com.aionemu.gameserver.model.gameobjects.VisibleObject#notSee(com.aionemu.gameserver.model.gameobjects.VisibleObject, boolean)
	 */
	@Override
	public void notSee(VisibleObject object, boolean isOutOfRange)
	{
		super.notSee(object, isOutOfRange);
		if (getKnownList().getKnownPlayers().isEmpty())
		{
			BindPoinTaskManager.getInstance().stopTask(this);
			players.clear();
		}
	}

	@Override
	public void run()
	{
		for (Player player : getKnownList().getKnownPlayers().values())
			if (MathUtil.isInRange(player, this, 25))
			{
				if (!players.contains(player))
				{
					players.add(player);
					if (player.getBindPoint() == null || player.getBindPoint().getMapId() != this.getWorldId()
							|| MathUtil.getDistance(player.getBindPoint().getX(), player.getBindPoint().getY(), getX(), getY()) < 25
							|| !canBind(player))
						continue;
						PacketSendUtility.sendPacket(player, SM_SYSTEM_MESSAGE.STR_NOTIFY_RESURRECT_POINT);
				}
			}
			else if (players.contains(player))
				players.remove(player);
	}

	/**
	 * @param objId
	 * @param spawnTemplate
	 * @param position
	 */
	public BindPoint(int objId, SpawnTemplate spawnTemplate)
	{
		super(objId, spawnTemplate);
		bindPointTemplate = DataManager.BIND_POINT_DATA.getBindPointTemplate(spawnTemplate.getTemplateId());
	}

	/* (non-Javadoc)
	 * @see com.aionemu.gameserver.model.gameobjects.interfaces.IDialog#onDialogRequest(com.aionemu.gameserver.model.gameobjects.player.Player)
	 */
	@Override
	public void onDialogRequest(Player player)
	{

		if (bindPointTemplate == null)
		{
			log.info("There is no bind point template for npc: " + this.getObjectTemplate().getTemplateId());
			return;
		}

		if (player.getBindPoint() != null && player.getBindPoint().getMapId() == this.getWorldId()
				&& MathUtil.getDistance(player.getBindPoint().getX(), player.getBindPoint().getY(), getX(), getY()) < 20)
		{
			PacketSendUtility.sendPacket(player, SM_SYSTEM_MESSAGE.STR_ALREADY_REGISTER_THIS_RESURRECT_POINT());
			return;
		}

		WorldType worldType = World.getInstance().getWorldMap(player.getWorldId()).getWorldType();

		if (worldType == WorldType.PRISON || !canBind(player))
		{
			PacketSendUtility.sendPacket(player, SM_SYSTEM_MESSAGE.STR_CANNOT_REGISTER_RESURRECT_POINT_FAR_FROM_NPC);
			return;
		}
		else
		{
			bindHere(player);
		}
	}

	private boolean canBind(Player player)
	{
		TribeClass tribe = ((NpcTemplate)getObjectTemplate()).getTribe();
		if (tribe != null)
		{
			switch (tribe)
			{
				case FIELD_OBJECT_DARK:
					if (player.getObjectTemplate().getRace() == Race.ELYOS)
						return false;
					break;
				case FIELD_OBJECT_LIGHT:
					if (player.getObjectTemplate().getRace() == Race.ASMODIANS)
						return false;
					break;
				case FIELD_OBJECT_ALL:
					return true;
				default:
					return false;
			}
		}
		return true;
	}

	private void bindHere(Player player)
	{
		RequestResponseHandler responseHandler = new RequestResponseHandler(this)
		{
			@Override
			public void acceptRequest(VisibleObject requester, Player responder)
			{
				if ((responder.getBindPoint() == null || responder.getBindPoint().getMapId() != requester.getWorldId() || MathUtil.getDistance(responder.getBindPoint().getX(), responder
						.getBindPoint().getY(), getX(), getY()) > 20)
						&& MathUtil.getDistance(requester, responder) <= ((NpcTemplate) requester.getObjectTemplate()).getTalkingDistance()
						&& canBind(responder))
				{
					if (Item.decreaseKinah(responder, bindPointTemplate.getPrice()))
					{
						boolean insert = false;
						if (responder.getBindPoint() == null)
							insert = true;
						
						responder.setBindPoint(new BindPointPosition(responder.getWorldId(), responder.getX(), responder.getY(), responder.getZ(), responder
								.getHeading()));
						if (insert)
							DAOManager.getDAO(PlayerBindPointDAO.class).insertBindPoint(responder);
						else
							DAOManager.getDAO(PlayerBindPointDAO.class).updateBindPoint(responder);
						
						TeleportService.sendSetBindPoint(responder);
						PacketSendUtility.broadcastPacket(responder, new SM_LEVEL_UPDATE(responder.getObjectId(), 2, responder.getObjectTemplate().getLevel()),
								true);
						PacketSendUtility.sendPacket(responder, SM_SYSTEM_MESSAGE.STR_DEATH_REGISTER_RESURRECT_POINT());
					}
					else
					{
						PacketSendUtility.sendPacket(responder, SM_SYSTEM_MESSAGE.STR_CANNOT_REGISTER_RESURRECT_POINT_NOT_ENOUGH_FEE());
						return;
					}
				}
			}

			@Override
			public void denyRequest(VisibleObject requester, Player responder)
			{
				//do nothing
			}
		};

		boolean requested = player.getResponseRequester().putRequest(SM_QUESTION_WINDOW.STR_BIND_TO_LOCATION, responseHandler);
		if (requested)
		{
			String price = Integer.toString(bindPointTemplate.getPrice());
			PacketSendUtility.sendPacket(player, new SM_QUESTION_WINDOW(SM_QUESTION_WINDOW.STR_BIND_TO_LOCATION, 0, price));
		}
	}
}
